#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<string.h>
#include<time.h>
#include"openssl/md5.h"                  //install openssl at first
#include<errno.h>
#include<fcntl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <assert.h>
//#include <stdbool.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include<getopt.h>

#define oops(m,x){ perror(m);exit(x);}

#define ACCESSPORT      1812
#define ACCOUNTINGPORT  1813
#define ACCESSACCEPT     0x02
#define ACCOUNTINGRESPONSE   0x05
#define ACCESSREJECT         0x03

/*if your gateway don't like xxx.xx.xx.1  there are something you should change accounding your  condition*/
#define GATEWAYLENGTH        11              //length of your gateway, for example, 172.19.119.1 is 12, and 172.119.119.1 is 13
#define FRAMEDROUTELENGTH    0x0d            //hex value of (gatewaylength + 2) ,for example 172.19.119.1 is 0x0e, and 172.119.119.1 is 0x0f
#define DEFAULTGATEWAY       TRUE                 //if your gateway is't ***.**.**.1 , you shuld't set this line

/*一些构建包的时候要用到数据*/
unsigned char Identifier;
unsigned char Authenticator[16];
unsigned char UserName[10] = {};
unsigned char CallingStationId[14] = {0x00,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x00};
unsigned char AcctSessionId[32];

unsigned char Password[30] = {};
unsigned char ChapId = 0x10;
unsigned char ChapPassword[17]={0x10,};
unsigned char ChapChallenge[32]; //= {0x34, 0x30, 0x38, 0x63, 0x65, 0x62, 0x64, 0x66, 0x35, 0x65, 0x32, 0x38, 0x34, 0x39, 0x61, 0x64,
                                   //0x65, 0x64, 0x63, 0x38, 0x38, 0x31, 0x35, 0x38, 0x33, 0x35, 0x38, 0x36, 0x65, 0x37, 0x34, 0x64};
unsigned char FramedIpAddress[4];
unsigned char FramedRoute[GATEWAYLENGTH] = {0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x31};
unsigned int acct_session_time = 0;

int offline;    //保持在线标识
int visit_school_resource;        //only visit the school resource flag

/* some structure of get commendline paraments */
char* const short_options = "u:p:g:ishv";

struct option long_options[] = {
     { "username",      1,      NULL,       'u'     },
     { "password",      1,      NULL,       'p'     },
     { "gateway",       1,      NULL,       'g'     },
     { "internet",      0,      NULL,       'i'     },
     { "school",        0,      NULL,       's'     },
     { "help",          0,      NULL,       'h'     },
     { "verbose",       0,      NULL,       'v'     },
     {      0,          0,      0,          0       },
};

/*sock and internet address*/
int sock;
char type;
struct sockaddr_in saddr;
socklen_t saddrlen;         //saddr length

struct access_request
{
    unsigned char access_request;    //接入请求报文 0x01
    unsigned char identifier;   //发送接入请求报文时，该值为一字节随机值，用以标识，其他时候为0
    unsigned char length[2]; //包长度 161字节 0x00a1
    unsigned char authenticator[16]; //随机16字节认证字

    unsigned char user_name;         //用户名 0x01
    unsigned char user_name_length;      //11字节 0x0b
    unsigned char user_name_value[9];   //研究生帐号可能需要修改
    unsigned char nas_identifier;        //nas 标识符 0x20
    unsigned char nas_identifier_length;  //18字节 0x12
    unsigned char nas_identifier_value[16];  //eCampus2.0.40.16
    unsigned char calling_station_id;      //对话标识ID 0x1f
    unsigned char calling_station_id_length;  //16字节 0x10
    unsigned char calling_station_id_value[14];  //为mac地址
    unsigned char chap_password;            //chap验证密码 0x03
    unsigned char chap_password_length;     //19字节 0x13
    unsigned char chap_id;                  //chap_id 为0x10
    unsigned char chap_password_value[16];  //md5[chapid+password+challenge]
    unsigned char chap_challenge;          //挑战字 0x3c
    unsigned char chap_challenge_length;   //36字节 0x22
    unsigned char chap_challenge_value[32];  //32字节随机值
    unsigned char broadcast;                 //广播地址 0x0a
    unsigned char broadcast_length;          //6字节  0x06
    unsigned char broadcast_value[4];       //0x00000001 0.0.0.1
    unsigned char framed_ip_address;        //ip地址 0x08
    unsigned char framed_ip_address_length;  //6字节 0x06
    unsigned char framed_ip_address_value[4];
    unsigned char framed_ip_netmask;          //子网掩码 0x09
    unsigned char framed_ip_netmask_length;   //0x06
    unsigned char framed_ip_netmask_value[4];    //0xffffff00 255.255.255.0
    unsigned char framed_route;               // 本地路由 0x16
    unsigned char framed_route_length;        //13字节，路由地址由分立的数字组成，以.分开
    unsigned char framed_route_value[GATEWAYLENGTH];    //0x31,0x37,0x32,0x2e,0x31,0x39,0x31,0x2e,0x31 172.19.91.1
    unsigned char service_type;              //服务类型 0x06
    unsigned char service_type_length;       //0x06
    unsigned char service_type_value[4];          //0x00,0x00,0x00,0x02  framed_user类型
    unsigned char framed_protocol;          //帧协议 0x07
    unsigned char framed_protocol_length;    //0x06
    unsigned char framed_protocol_value[7];       //0x00,0x00,0x00,0x01 PPP协议
};

struct accounting_request
{
    unsigned char accounting_request;   //0x04 计费请求包
    unsigned char packet_identifier;     //包标识字 0x00
    unsigned char length[2];            //包长度 0x00,0x81 129字节
    unsigned char authenticator[16];     //认证字 全为零

    unsigned char user_name;         //用户名 0x01
    unsigned char user_name_length;      //11字节 0x0b
    unsigned char user_name_value[9];   //研究生帐号可能需要修改
    unsigned char nas_identifier;        //nas 标识符 0x20
    unsigned char nas_identifier_length;  //18字节 0x12
    unsigned char nas_identifier_value[16];  //eCampus2.0.40.16
    unsigned char calling_station_id;      //对话标识ID 0x1f
    unsigned char calling_station_id_length;  //16字节 0x10
    unsigned char calling_station_id_value[14];  //为mac地址
    unsigned char acct_status_type;           //请求状态 0x28
    unsigned char acct_status_type_length;    //0x06
    unsigned char acct_status_type_value[4];   //0x00,0x00,0x00,0x01  start(1)状态
    unsigned char framed_ip_address;        //ip地址 0x08
    unsigned char framed_ip_address_length;  //6字节 0x06
    unsigned char framed_ip_address_value[4];
    unsigned char acct_session_id;            //会话标识id 0x2c 与ip地址有关
    unsigned char acct_session_id_length;      //0x22 34字节
    unsigned char acct_session_id_value[32];   //32字节
    unsigned char acct_authentic;               //接入认证 0x2d
    unsigned char acct_authentic_length;         //0x06
    unsigned char acct_authentic_value[4];       //0x00,0x00,0x00,0x01  radius类型
    unsigned char nas_port_type;                 //nas端口类型 0x3d
    unsigned char nas_port_type_length;         //0x06
    unsigned char nas_port_type_value[4];        //0x00,0x00,0x00,0x0f ethernet(15)类型
    unsigned char nas_port;                     //nas 端口 0x05
    unsigned char nas_port_length;              //0x06
    unsigned char nas_port_value[4];            //0x00,0x00,0x00,0x03 为国内外资源，0x00,0x00,0x00,0x01 为校内资源

};

struct accounting_request_update            //keep  online packet
{
    unsigned char accounting_request;   //0x04 计费请求包
    unsigned char packet_identifier;     //包标识字 0x00
    unsigned char length[2];            //包长度 0x00,0x81 129字节
    unsigned char authenticator[16];     //认证字 全为零

    unsigned char user_name;         //用户名 0x01
    unsigned char user_name_length;      //11字节 0x0b
    unsigned char user_name_value[9];   //研究生帐号可能需要修改
    unsigned char nas_identifier;        //nas 标识符 0x20
    unsigned char nas_identifier_length;  //18字节 0x12
    unsigned char nas_identifier_value[16];  //eCampus2.0.40.16
    unsigned char calling_station_id;      //对话标识ID 0x1f
    unsigned char calling_station_id_length;  //16字节 0x10
    unsigned char calling_station_id_value[14];  //为mac地址
    unsigned char acct_status_type;           //请求状态 0x28
    unsigned char acct_status_type_length;    //0x06
    unsigned char acct_status_type_value[4];   //0x00,0x00,0x00,0x03  interim_update状态
    unsigned char framed_ip_address;        //ip地址 0x08
    unsigned char framed_ip_address_length;  //6字节 0x06
    unsigned char framed_ip_address_value[4];
    unsigned char acct_session_id;            //会话标识id 0x2c 与ip地址有关
    unsigned char acct_session_id_length;      //0x22 34字节
    unsigned char acct_session_id_value[32];   //32字节
    unsigned char acct_authentic;               //接入认证 0x2d
    unsigned char acct_authentic_length;         //0x06
    unsigned char acct_authentic_value[4];       //0x00,0x00,0x00,0x01  radius类型
    unsigned char nas_port_type;                 //nas端口类型 0x3d
    unsigned char nas_port_type_length;         //0x06
    unsigned char nas_port_type_value[4];        //0x00,0x00,0x00,0x0f ethernet(15)类型

    unsigned char nas_port;                     //nas 端口 0x05
    unsigned char nas_port_length;              //0x06
    unsigned char nas_port_value[4];            //0x00,0x00,0x00,0x03 为国内外资源，0x00000001 为校内资源
    unsigned char acct_session_time;            //对话维持时间 0x2e
    unsigned char acct_session_time_length;      //0x06
    unsigned char acct_session_time_value[4];     //每隔100秒发出一个对话维持包，该字段自增100
};

/**********************************************************************************
 * 停止连接包与维持连接包类似，acct-status_type_value为0x00,0x00,0x00,0x02 stop(2)*
 * ********************************************************************************/

struct response
{
    unsigned char code;   //0x02 for access_accept, 0x05 for accounting_response, 0x03 for access_reject
    unsigned char packet_identifier;    //0x00
    unsigned char length[2];
    unsigned char authenticator[16];       //16字节认证字
    unsigned char reply_message;          //reply message from server
    unsigned char reply_message_length;
    unsigned char reply_message_value[3];

};


/** Allocate memory, abort immediately if we cannot **/
void* safe_malloc(size_t size)
{
	void *p;

	if (!(p = malloc(size))) {
		perror("FATAL: Could not allocate memory");
		exit(1);
	}
	return p;
}


/** Get reasonably good random data **/

#define RANDOM_DEV	"/dev/urandom"

static void get_random_data(char *p, ssize_t len)
{
#ifdef RANDOM_DEV
	int fd;
	ssize_t n;

	if ((fd = open(RANDOM_DEV, O_RDONLY)) != -1 &&
	    (n = read(fd, p, len)) == len &&
	    close(fd) != -1) return;

        oops("Warning: No data from " RANDOM_DEV ", using rand()\n",2);
#endif
	while(len-- > 0) *p++ = (char)(256.0 * rand() / (RAND_MAX + 1.0));
}

/** to get local ip address **/
int get_local_ip(char *name, unsigned char *local_ip)
{
	struct ifreq ifr;
	int sock;

	if(name == NULL || local_ip == NULL){
		printf("get_local_ip: NULL para\n");
		return -1;
	}

 sock = socket(AF_INET, SOCK_STREAM, 0);
	if(sock < 0){
		printf("get_local_ip: socket error\n");
		//return -1;
	}

	//get eth1 mac addr
	memset(local_ip, 0, 4);
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, name, 6);
	if (ioctl(sock, SIOCGIFADDR, &ifr) < 0){
			perror("get_local_ip ioctl");
			close(sock);
			return -1;
	} else {
			memcpy(local_ip, (ifr.ifr_addr.sa_data+2), 4);
			//printf("local_ip: %2x : %2x : %2x : %2x\n", local_ip[0], local_ip[1], local_ip[2], local_ip[3]);
	}

	close(sock);
	return 0;
}

/**
 * get_hwaddr - get netdevice mac addr
 * @name: device name, e.g: eth0
 * @hwaddr: where to save mac, 6 byte hwaddr[6]
 * @return: 0 on success, -1 on failure
 */

int get_hwaddr(char *name, unsigned char *hwaddr)
{
	struct ifreq ifr;
	int sock;

	if(name == NULL || hwaddr == NULL){
		printf("get_hwaddr: NULL para\n");
		return -1;
	}

 sock = socket(AF_INET, SOCK_STREAM, 0);
	if(sock < 0){
		printf("get_hwaddr: socket error\n");
		//return -1;
	}

	//get eth1 mac addr
	memset(hwaddr, 0, 6);
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, name, 6);
	if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0){
			perror("get_hwaddr ioctl");
			close(sock);
			return -1;
	} else {
			memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, 6);
			//printf("hwaddr: %2x : %2x : %2x : %2x : %2x : %2x\n", hwaddr[0], hwaddr[1],hwaddr[2], hwaddr[3],hwaddr[4], hwaddr[5]);
	}

	close(sock);
	return 0;
}

int trans_hex_to_char(char mac_string[], unsigned char byte_arr[], int arr_len)  //transfrom hex type to char type, used in CallingStationId
{
    char* hexstr = mac_string;
    char* hexstrtail = hexstr;

    int i =0;
    for (i=0;i<arr_len;i++)
    {
        char hex1;
        char hex2;
        int value=byte_arr[i]; //直接将unsigned char赋值给整型的值，系统会正动强制转换
        int v1=value/16;
        int v2=value % 16;

        //将商转成字母
        if (v1>=0&&v1<=9)
            hex1=(char)(48+v1);
        else
            hex1=(char)(87+v1);

        //将余数转成字母
        if (v2>=0&&v2<=9)
            hex2=(char)(48+v2);
        else
            hex2=(char)(87+v2);

        //将字母连接成串
        //*hexstr=*hexstr+hex1+hex2;
        strcpy(hexstrtail, &hex1);
        strcpy(hexstrtail+1, &hex2);
        hexstrtail = hexstrtail + 2;

    }

    return 0;
}

/** function of send access_request packet **/
int send_access_request(int sock, struct sockaddr_in saddr, socklen_t saddrlen )
{

  struct access_request AccessRquest = {
  0x01,        //access_raquest
  0x66,        //identifier
  0x00, 0xa1,   //length
  0xb2, 0xab, 0x40, 0xbc, 0xd0, 0x09, 0x3d, 0xc0, 0x7b, 0xe1, 0xaf, 0x07, 0x71, 0x3e, 0xf7, 0xec,    //authenticator
  0x01,          //user_name
  0x0b,          //user_name_length
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //username
  0x20,          //nas
  0x12,            //length
  0x65, 0x43, 0x61, 0x6d, 0x70, 0x75, 0x73, 0x32, 0x2e, 0x30, 0x2e, 0x34, 0x30, 0x2e, 0x31, 0x36,  //eCampus2.0.40.16
  0x1f,             //calling_station_id
  0x10,             //length 16
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  //mac
  0x03,             //chap_password
  0x13,             //length
  0x10,             //chap_id
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   //chap_password
  0x3c,              //chap_challenge
  0x22,               //length
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   //chap_challenge
  0x0a,               //broadcast
  0x06,               //length
  0x00,0x00,0x00,0x01,     //0.0.0.1
  0x08,                     //framed_ip_address
  0x06,                     //length
  0x00, 0x00, 0x00, 0x00,    //172.19.91.195
  0x09,                      //net_mask
  0x06,                      //length
  0xff, 0xff, 0xff, 0x00,     //255.255.255.0
  0x16,                       //route
  0x0d,                       //length
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   /****gateway  number of the 0x00 should be your gateway length ******/
  0x06,                       //service_type
  0x06,                        //length
  0x00, 0x00, 0x00, 0x02,       //framed_user
  0x07,                         //framed_protocol
  0x06,                         //length
  0x00, 0x00, 0x00, 0x01,       //ppp protocl
  };

  memcpy(&AccessRquest.identifier, &Identifier, 1);
  memcpy(AccessRquest.authenticator, Authenticator, 16);
  memcpy(AccessRquest.user_name_value, UserName, 9);
  memcpy(AccessRquest.calling_station_id_value, CallingStationId, 14);      //init the calling_station_id
  memcpy(&AccessRquest.chap_id, &ChapId, 1);
  memcpy(AccessRquest.chap_password_value, ChapPassword, 16);
  memcpy(AccessRquest.chap_challenge_value, ChapChallenge, 32);
  memcpy(AccessRquest.framed_ip_address_value, FramedIpAddress, 4);

  AccessRquest.framed_route_length = FRAMEDROUTELENGTH;                    //length of your gateway
  memcpy(AccessRquest.framed_route_value, FramedRoute, GATEWAYLENGTH);

  sendto(sock, &AccessRquest, sizeof(AccessRquest), 0, (struct sockaddr *)&saddr, saddrlen);

  return 0;
}

/** function of send accounting_request packet**/
int send_accounting_request(int sock, struct sockaddr_in saddr, socklen_t saddrlen)
{
    struct accounting_request AccountingRequest ={
    0x04, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
    0x12, 0x65, 0x43, 0x61, 0x6d, 0x70, 0x75, 0x73,
    0x32, 0x2e, 0x30, 0x2e, 0x34, 0x30, 0x2e, 0x31,
    0x36, 0x1f, 0x10, 0x36, 0x30, 0x65, 0x62, 0x2d,
    0x36, 0x39, 0x61, 0x63, 0x2d, 0x63, 0x64, 0x61,
    0x33, 0x28, 0x06, 0x00, 0x00, 0x00, 0x01, 0x08,
    0x06, 0xac, 0x13, 0x5b, 0xc3, 0x2c, 0x22, 0x61,
    0x37, 0x66, 0x65, 0x31, 0x36, 0x30, 0x31, 0x38,
    0x63, 0x64, 0x31, 0x37, 0x64, 0x39, 0x36, 0x5f,
    0x31, 0x65, 0x38, 0x39, 0x66, 0x33, 0x65, 0x62,
    0x64, 0x32, 0x30, 0x38, 0x30, 0x30, 0x61, 0x2d,
    0x06, 0x00, 0x00, 0x00, 0x01, 0x3d, 0x06, 0x00,
    0x00, 0x00, 0x0f, 0x05, 0x06, 0x00, 0x00, 0x00,
    0x03,
    };

    memcpy(AccountingRequest.user_name_value, UserName, 9);
    memcpy(AccountingRequest.calling_station_id_value, CallingStationId, 14);      //init the calling_station_id
    memcpy(AccountingRequest.framed_ip_address_value, FramedIpAddress, 4);
    memcpy(AccountingRequest.acct_session_id_value, AcctSessionId, 32);

    if(visit_school_resource == 1)
        AccountingRequest.nas_port_value[3] = 0x01;

    sendto(sock, &AccountingRequest, sizeof(AccountingRequest), 0, (struct sockaddr *)&saddr, saddrlen);
    return 0;
}

/** function of send keep online packet **/
int send_accounting_request_update(int sock, struct sockaddr_in saddr, socklen_t saddrlen)
{
    struct accounting_request_update AccountingRequestUpdate = {
    0x04, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
    0x12, 0x65, 0x43, 0x61, 0x6d, 0x70, 0x75, 0x73,
    0x32, 0x2e, 0x30, 0x2e, 0x34, 0x30, 0x2e, 0x31,
    0x36, 0x1f, 0x10, 0x36, 0x30, 0x65, 0x62, 0x2d,
    0x36, 0x39, 0x61, 0x63, 0x2d, 0x63, 0x64, 0x61,
    0x33, 0x28, 0x06, 0x00, 0x00, 0x00, 0x03, 0x08,
    0x06, 0xac, 0x13, 0x5b, 0xc3, 0x2c, 0x22, 0x61,
    0x37, 0x66, 0x65, 0x31, 0x36, 0x30, 0x31, 0x38,
    0x63, 0x64, 0x31, 0x37, 0x64, 0x39, 0x36, 0x5f,
    0x31, 0x65, 0x38, 0x39, 0x66, 0x33, 0x65, 0x62,
    0x64, 0x32, 0x30, 0x38, 0x30, 0x30, 0x61, 0x2d,
    0x06, 0x00, 0x00, 0x00, 0x01, 0x3d, 0x06, 0x00,
    0x00, 0x00, 0x0f, 0x05, 0x06, 0x00, 0x00, 0x00,
    0x03, 0x2e, 0x06, 0x00, 0x00, 0x00, 0x00,
    };
    AccountingRequestUpdate.acct_status_type_value[3] = 0x03;
    unsigned char buf[4]={0x00, 0x00, 0x00, 0x00};
    int i = 0;
    acct_session_time += 100;
    printf("%d\n",acct_session_time);
    memcpy(buf, &acct_session_time, 4);

    for (i =0; i<4; i++)
    {
        memcpy(&AccountingRequestUpdate.acct_session_time_value[i], &buf[3 - i], 1);

    }

    if(visit_school_resource == 1)
        AccountingRequestUpdate.nas_port_value[3] = 0x01;         //visit the school resource

    memcpy(AccountingRequestUpdate.user_name_value, UserName, 9);
    memcpy(AccountingRequestUpdate.calling_station_id_value, CallingStationId, 14);      //init the calling_station_id
    memcpy(AccountingRequestUpdate.framed_ip_address_value, FramedIpAddress, 4);
    memcpy(AccountingRequestUpdate.acct_session_id_value, AcctSessionId, 32);

    sendto(sock, &AccountingRequestUpdate, sizeof(AccountingRequestUpdate), 0, (struct sockaddr *)&saddr, saddrlen);

    return 0;
}

/** function of send stop connect packet**/
int  send_stop_request(int sock, struct sockaddr_in saddr, socklen_t saddrlen)
{
    struct accounting_request_update StopRequest = {
    0x04, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
    0x12, 0x65, 0x43, 0x61, 0x6d, 0x70, 0x75, 0x73,
    0x32, 0x2e, 0x30, 0x2e, 0x34, 0x30, 0x2e, 0x31,
    0x36, 0x1f, 0x10, 0x36, 0x30, 0x65, 0x62, 0x2d,
    0x36, 0x39, 0x61, 0x63, 0x2d, 0x63, 0x64, 0x61,
    0x33, 0x28, 0x06, 0x00, 0x00, 0x00, 0x02, 0x08,
    0x06, 0xac, 0x13, 0x5b, 0xc3, 0x2c, 0x22, 0x61,
    0x37, 0x66, 0x65, 0x31, 0x36, 0x30, 0x31, 0x38,
    0x63, 0x64, 0x31, 0x37, 0x64, 0x39, 0x36, 0x5f,
    0x31, 0x65, 0x38, 0x39, 0x66, 0x33, 0x65, 0x62,
    0x64, 0x32, 0x30, 0x38, 0x30, 0x30, 0x61, 0x2d,
    0x06, 0x00, 0x00, 0x00, 0x01, 0x3d, 0x06, 0x00,
    0x00, 0x00, 0x0f, 0x05, 0x06, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    };

    StopRequest.acct_status_type_value[3] = 0x02;

    unsigned char buf[4];
    int i = 0;
    memcpy(buf, &acct_session_time, 4);

    for (i = 0; i<4; i++)                //init the acct_session_time, 时间值在包中的存放顺序与int中的相反，所以需要调整
    {
        memcpy(&StopRequest.acct_session_time_value[i], &buf[3 - i], 1);

    }

    memcpy(StopRequest.user_name_value, UserName, 9);
    memcpy(StopRequest.calling_station_id_value, CallingStationId, 14);      //init the calling_station_id
    memcpy(StopRequest.framed_ip_address_value, FramedIpAddress, 4);
    memcpy(StopRequest.acct_session_id_value, AcctSessionId, 32);

    sendto(sock, &StopRequest, sizeof(StopRequest), 0, (struct sockaddr *)&saddr, saddrlen);

    return 0;
}
int  rece_response(int sock, struct sockaddr_in saddr)
{
    struct response Response;

    if(recvfrom(sock, &Response, sizeof(Response), 0, 0, 0) == -1)
    {
        oops("No response from the server",0);
    }
    if(Response.code != ACCESSACCEPT && Response.code != ACCOUNTINGRESPONSE || Response.code == ACCESSREJECT)
    {
        if(Response.reply_message_value[2] == 0x02)
        {
            oops("Not enough money in the count! Pleased charged soon.\n", 0);
        }
        else if(Response.reply_message_value[2] == 0x03)
        {
            oops("Your are not in the allowed visit time, the internet will close after 23:15.\n", 0);
        }
    }

    return Response.code;
}

/** 校验和函数，接受三个参数，第一个是加密后的数据存放位置，第二个是要加密的数据*****
****第三个是标识值，设为1时，将输出32字节的char类型数据，否则是16字节16进制的数据 **/
int md5_sum( unsigned char* trans_result, unsigned char* data, int flag)
{
    MD5_CTX ctx;
    unsigned char md[16];
    char tmp[3]={'w','w','\0'};
    int i;

    MD5_Init(&ctx);
    MD5_Update(&ctx, data, strlen((char *)data));
    MD5_Final(md, &ctx);

    if ( flag == 1)
    {
        for( i=0; i<16; i++ )
        {
            sprintf(tmp,"%2x", md[i]);
            memcpy((trans_result+i*2), tmp, 2);
            //printf("%s\n", tmp);
        }
    }
    else
    {
         memcpy(trans_result, md, 16);
    }

    return 0;
}

int set_offline_flag(void)
{
    offline = 1;
    return 0;
}
 /** init some usefull data **/
int para_init(void)
{
    char * device_name = "eth0";
    unsigned char mac[6] = {};           //mac address
    char random_buf[32] = {};             // a random buffer, provide data for md5
    char mac_string[12];                  // a mac buffer for char type data of mac
    int  gateway[4];

    srand(time(0));                       // set seed of rand() function
    Identifier = rand();                   // get one byte random data, to init the Identifier

    get_hwaddr(device_name, mac);                   //get mac address data
    if(get_local_ip("eth0", FramedIpAddress) != 0)          //get ip address
    {
        get_local_ip("wlan0", FramedIpAddress);
    }

    trans_hex_to_char(mac_string, mac, 6);

    /** to init the CallingStation with specical form : xxxx-xxxx-xxxx **/
    int i,j,k;
    for(i = 0, j = 0; i<=9; i+=4, j+=5)
    {
        for(k = 0; k<4; k++)
        {
            memcpy(&CallingStationId[j+k], &mac_string[i+k], 1);
        }

    }

    get_random_data(random_buf, 32);

    md5_sum(Authenticator, (unsigned char*) random_buf, 0);    //init the Authenticator with 16byte md5sum type random data
    md5_sum(ChapChallenge, (unsigned char*) random_buf, 1);    //init the Chapchanllenge with 32byte mad5sum type random data
    //printf("%s\n", random_buf);
    //printf("%s\n", ChapChallenge);

    unsigned char buf[60] = {};

    memcpy(buf, &ChapId, 1);
    memcpy((buf+1), Password, strlen((char* )Password));
    memcpy((buf+1+strlen((char* )Password)), ChapChallenge, 32);

    //printf("%s\n", buf);

    md5_sum((ChapPassword), buf, 0);                   //init the ChapPassword with md5(ChapId + password + ChapChanllenge)

/** init the AcctSessionId , first 16 byte is the md5 of ip address, then is '_', last 15 byte is random data **/
    md5_sum(AcctSessionId, FramedIpAddress, 1);        //init the first 16 byte AcctSessionId with md5 of ip address
    AcctSessionId[16] = 0x5f;                          //AcctSessionId[16] is '_'
    memcpy((AcctSessionId+17), ChapChallenge, 15);     //to init the AcctSession with specical form xxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxx.

/** set the FramedRoute manually **/
#ifdef DEFAULTGATEWAY
    for (i = 0; i< 3; i++)
        gateway[i] = (int ) FramedIpAddress[i];
    FramedRoute[0] = (unsigned char) (gateway[0]/100 + 48);
    FramedRoute[1] = (unsigned char) (gateway[0]%100/10 + 48);
    FramedRoute[2] = (unsigned char) (gateway[0]%10 + 48);
    FramedRoute[4] = (unsigned char) (gateway[1]/10 + 48);
    FramedRoute[5] = (unsigned char) (gateway[1]%10 + 48);
    FramedRoute[7] = (unsigned char) (gateway[2]/10 + 48);
    FramedRoute[8] = (unsigned char) (gateway[2]%10 + 48);
#endif

    return 0;
}

int main(int argc, char* argv[])
{
     /** get the commendline paraments **/
     int c;
     while((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1)
     {
         switch (c)
         {
         case 'u':
             memcpy(UserName, optarg, 9);
             break;
         case 'p':
             memcpy(Password, optarg, strlen((char *)optarg));
             break;
         case 'g':
             memcpy(FramedRoute, optarg, strlen((char *)optarg));
             break;
         case 's':
             visit_school_resource = 1;
             break;
         case 'i':
             visit_school_resource = 0;
             break;
         case 'v':
             printf("ecampus4linux1.0\t2011/04/01\n"
                    "Auther:fengyanfengyu\temail:fenglinwansu@gmail.com\n");
             break;
         case 'h':
             printf(
            "Usage: ecampus4linux [-u username ] [-p password] [-g gateway]\n"
            "[-s only visit the school resource] [-i visit internet resource] \n"
            "Options:\n\n"
            "  -u   your username , only support 0510xxxxx or so\n"
            "  -p   your password , dont be too long \n"
            "  -g   your gateway address, if your gateway don't like ***.**.**.1 , you should give this parament\n"
            "  -s   only visit the school resource, ftp for example\n"
            "  -i   visit both school and intetnet resource, this is default option\n"
            "  -v	verbose\n"
            "  -h	dispaly this help\n");
            break;
        case '?':
             printf(
              "Usage: ecampus4linux [-u username ] [-p password] [-g gateway]\n"
            "[-s only visit the school resource] [-i visit internet resource] \n"
            "Options:\n\n"
            "  -u   your username , only support 0510xxxxx or so\n"
            "  -p   your password , dont be too long \n"
            "  -g   your gateway address, if your gateway don't like ***.**.**.1 , you should give this parament\n"
            "  -s   only visit the school resource, ftp for example\n"
            "  -i   visit both school and intetnet resource, this is default option\n"
            "  -v	verbose\n"
            "  -h	dispaly this help\n");
            break;
         }
     }

    /** set sock address of radius server **/
    sock = socket(PF_INET, SOCK_DGRAM, 0);
    bzero( (void* )&saddr, sizeof(saddr) );

    saddr.sin_addr.s_addr=inet_addr("202.119.64.86");
    saddr.sin_port = htons(ACCESSPORT);
    saddr.sin_family = AF_INET;
    saddrlen = sizeof(saddr);

    //** init data **/
    para_init();

    // capture the [ctrl]+C signal to stop connect and exit safely
    signal( SIGINT, (void*) set_offline_flag );

    // send packet now
    send_access_request( sock, saddr, saddrlen );
    if( rece_response(sock, saddr) != ACCESSACCEPT)
       oops("Connect error, please check your username and password\n",0);
    saddr.sin_port = htons(ACCOUNTINGPORT);

    send_accounting_request( sock, saddr, saddrlen);
    if( rece_response(sock, saddr) != ACCOUNTINGRESPONSE)
    {
           oops("Connect error, please check your username and password\n",0);
    }
    else
    {
        printf("Connect Success, now your can visit the internet.\n");
    }

    // send keep update packet every 100 second
    while( offline == 0)
    {
        send_accounting_request_update( sock, saddr, saddrlen);
        sleep(100);
    }

    if(offline == 1)
    {
        send_stop_request(sock, saddr, saddrlen);
    }
     if( rece_response(sock, saddr) != ACCOUNTINGRESPONSE)
    {
           oops("Sorry, now we can't stop the connect.wait it disconnect automatically\n",0);
    }
    else
    {
        printf("Disconnect success, now you exit safely.\n");
    }

    //pause();
    close(sock);
    return 0;
}
